import { $service, $toast, $uploadBigFile } from '@/cross/utils';
import io from 'socket.io-client';
import { IMessage, ISession, IUser } from '../types';
import { IImAudio, IImFile, IImGeo, IImImage, IImVideo, IMessageFile, IScene } from '../types/base';
import { IImError } from '../types/error';
import { IBlackItem, IDeviceType, IImDeviceEvent, ILoginPort, ILoginPortOs, IMuteItem } from '../types/model/Device';
import { ISysMsgUnRead, ISystemMessage } from '../types/model/SystemMessage';
import { IServiceCloudImMsgSendParams, IServiceCloudImMsgSendResult, IServiceCloudImMsgFetchParams, IServiceCloudImMsgFetchResult, IServiceCloudImMsgDeleteParams } from '../types/service/Message';
import { IServiceCloudImSessionFetchParams, IServiceCloudImSessionFetchResult } from '../types/service/Session';
import uuid from './uuid';
import ListStore from '../../../utils/list';
import { IServiceCloudImUserFetchParams, IServiceCloudImUserFetchResult, IServiceCloudImUserUpdateParams } from '../types/service/User';
import { toJS } from 'mobx';
import { $getDeviceInfo } from '@/cross/utils/device';
import imageScale from './imageScale';

/**
 * 初始化MimSDK配置信息
 * @description 目前版本简单化将部分功能，如同步黑名单列表与即时接收其他端最新的黑名单操作，本应分为初始化时拉取完整信息，待机时仅即时接收某一单条信息,目的减少复杂度，在用户量规模不大的情况不影响用户体验.
 */
interface MimSdkConfig {
  debug?: boolean;
  server: string;
  account: string;
  token: string;
  client: string;
  deviceId: string;
  type: IDeviceType;
  os: ILoginPortOs;
  /** 是否启用本地数据库 */
  db?: boolean;
  fetchPushId?: () => Promise<string>;
  onconnect: () => void;
  onwillreconnect: () => void;
  /**
   * 302 账号或者密码错误, 请跳转到登录页面并提示错误
   * 417 重复登录, 已经在其它端登录了, 请跳转到登录页面并提示错误
   * kicked 被踢, 请提示错误后跳转到登录页面
   */
  ondisconnect: (error: { code: 302 | 417 | 'kicked' }) => void;
  onerror: (event: Error, res: object) => void;
  onconnectchange: (state: keyof typeof MimConnectState) => void;
  /**
   * 多端登录状态变化的回调, 会收到登录端列表, 以下情况会收到此回调
   */
  onloginportschange: (loginPorts: Array<ILoginPort>) => void;

  onuser: (user: IUser) => void;
  onusers: (usersMAp: { [propName: string]: IUser }) => void;

  onsessions: (sessions: Array<ISession>) => void;
  onupdatesession: (session: ISession) => void;
  ondeletesession: (scene: string, to: string) => void;
  /**
   * 是否同步会话消息未读数
   * * 如果选择同步
   *   * 那么在一个端读过的会话在其它端也会被标记为已读
   *   * 在调用MIM#setCurrSession的时候 SDK 会自动同步一次未读数, 此后如果收到当前会话的消息, 需要手动调用MIM#resetSessionUnread来同步未读数
   */
  syncSessionUnread?: boolean;

  /** 收到消息的回调 */
  onmsg?: (message: IMessage) => void;
  /**
   * 收到离线消息的回调
   * 根据判断本地存储消息的最后一条消息的时间戳拉取后回调
   */
  onofflinemsgs?: (messages: Array<IMessage>) => void;

  /** 收到系统消息的回调 */
  onsysmsg?: (message: ISystemMessage) => void;
  /**
   * 收到离线系统消息的回调
   * 根据判断本地存储消息的最后一条消息的时间戳拉取后回调
   */
  onofflinesysmsgs?: (messages: Array<ISystemMessage>) => void;
  /**
   * 更新系统通知后的回调, 会传入系统通知
      以下情况会收到此回调
      通过好友申请
      拒绝好友申请
      接受入群邀请
      拒绝入群邀请
      通过入群申请
      拒绝入群申请
      这些操作的发起方会收到此回调, 接收被更新的系统通知, 根据操作的类型系统通知会被更新为下面两种状态
      'passed': 已通过
      'rejected': 已拒绝
   */
  onupdatesysmsg?: (message: ISystemMessage) => void;

  oncustomsysmsg?: (sysCustomMsgs: ISystemMessage[]) => void;
  onofflinecustomsysmsgs?: (sysCustomMsgs: ISystemMessage[]) => void;

  /**
   * 收到系统通知未读数的回调
   * SDK 会管理内建系统通知的未读数, 此回调接收的对象包括以下字段
   * total: 总共的未读数
   * friend: 所有跟好友相关的系统通知的未读数
   * addFriend: 直接加为好友的未读数
   * applyFriend: 申请加为好友的未读数
   * passFriendApply: 通过好友申请的未读数
   * rejectFriendApply: 拒绝好友申请的未读数
   * deleteFriend: 删除好友的未读数
   * team: 所有跟群相关的系统通知的未读数
   * teamInvite: 入群邀请的未读数
   * rejectTeamInvite: 接受入群邀请的未读数
   * applyTeam: 入群申请的未读数
   * rejectTeamApply: 拒绝入群申请的未读数
   *
   */
  onsysmsgunread?: (sysMsgUnRead: ISysMsgUnRead) => void;

  /** 是否同步黑名单和静音列表 */
  syncRelations?: boolean;
  /** 黑名单同步和接收其他端变化后回调 Array<accid> */
  onblacklist?: (blacklist: Array<IBlackItem>) => void;
  /** 静音列表同步和接收其他端变化后回调 Array<accid> */
  onmutelist?: (mutelist: Array<IMuteItem>) => void;

  /** 我的IM个人信息变化后的回调 */
  onmyinfo?: (user: IUser) => void;
}

class Mim {
  constructor(config: MimSdkConfig) {
    this.config = config;
    this.init();
  }

  config: MimSdkConfig;
  socket: SocketIOClient.Socket | null = null;

  /** 初始化 */
  init() {
    const { server, account, token, client, deviceId, os, type, onconnect, onmsg, fetchPushId, onuser } = this.config;
    console.log('[server] ', server);
    const socket = io(server);

    // 开始连接
    this.onConnectChange('connecting');
    socket.on('opend', (data: any) => {
      console.log('imClient data: ', data);
      this.onConnectChange('connect');
    });
    socket.on('imevent', (data: IImDeviceEvent) => {
      console.log('[imevent] ', data);
      const { type } = data;
      if (type === 'login') {
        // 登录（鉴权）成功,回调
        onconnect();
        // 获取本人名片
        this.fetchUser([account]).then(({ usersMap }) => {
          // 回调更新本人用户信息
          onuser && onuser(usersMap[account]);
          // 获取静音&黑名单列表
          this.getMuteList();
          this.getBlackList();
          // 更新推送ID
          if (fetchPushId) {
            // 获取推送ID
            fetchPushId().then((push_id) => {
              console.log('[fetchPushId] ', push_id);
              let user = usersMap[account];
              let pushid = push_id + '|' + $getDeviceInfo().os;
              if (user.pushid !== pushid) {
                this.updateMyInfo({
                  pushid
                })
              }
            })
          }
        });
        // 同步会话
        this.syncSession();
      }
      // 收到新消息及会话 / 收到来自其他端的消息及会话
      if (data.type === 'msg' || data.type === 'syncmsg') {
        let _msg = this.formatMessage(data.msg);
        onmsg && onmsg(_msg);
        if (data.session && data.session.to) {
          this.fetchUser([data.session.to]).then(() => {
            this.config.onsessions(this.formatSessions([data.session]));
          });
        }
        // 消息接收回执（先判断消息是接收自其它用户，不是本人其它端）
        if (data.msg.from !== account) {
          this.sendMsgReceipt({ msg: data.msg })
        }
      }
      // 接收到session更新
      if (data.type === 'session') {
        if (data.session) {
          this.config.onupdatesession(this.formatSessions([data.session])[0]);
        }
      }
      // 接收到删除会话的通知
      if (data.type === 'onDeleteSession') {
        this.config.ondeletesession && this.config.ondeletesession(data.scene, data.to)
      }
    });
    socket.on('connect', () => {
      console.log('socket.io connect event');
      /**
       * 开始鉴权
       */
      socket.emit('imlogin', {
        client,
        account,
        token,
        deviceId,
        type,
        os,
      });
    });
    socket.on('disconnect', () => {
      console.log('socket.io disconnect event');
      this.onConnectChange('disconnect');
      // socket.io.reconnect(); // 已经设置了自动重连
    });
    socket.on('connect_error', () => {
      console.log('socket.io connect_error event');
      this.onConnectChange('connect_error');
    });
    socket.on('connect_timeout', () => {
      console.log('socket.io connect_timeout event');
      this.onConnectChange('connect_timeout');
    });
    socket.on('reconnect', () => {
      console.log('socket.io reconnect event');
      this.onConnectChange('reconnect');
    });
    socket.on('reconnect_attempt', () => {
      console.log('socket.io reconnect_attempt event');
      this.onConnectChange('reconnect_attempt');
    });
    socket.on('reconnecting', () => {
      console.log('socket.io reconnecting event');
      this.onConnectChange('reconnecting');
    });
    socket.on('reconnect_error', () => {
      console.log('socket.io reconnect_error event');
      this.onConnectChange('reconnect_error');
    });
    socket.on('reconnect_failed', () => {
      console.log('socket.io reconnect_failed event');
      this.onConnectChange('reconnect_failed');
    });
    this.socket = socket;
  }

  onConnectChange(state: keyof typeof MimConnectState) {
    console.log('[state change] ', state);
  }

  /**
   * 通过ws向云端发送事件
   */
  sendToServer = (eventMsg: IImDeviceEvent) => {
    eventMsg.emid = uuid();
    this.socket?.emit('imserverevent', eventMsg);
  };

  /**
   * 拉取用户名片信息
   */
  usersMap: { [propName: string]: IUser } = {};
  fetchUser = (accounts: string[]) => {
    return new Promise<{ usersMap: { [propName: string]: IUser } }>((resolve, reject) => {
      const { token, account, onusers } = this.config;
      // 已经存在的不再拉取
      let values = accounts.filter((account) => !this.usersMap[account]);
      if (values.length === 0) {
        return resolve({
          usersMap: this.usersMap,
        });
      }
      let params: IServiceCloudImUserFetchParams = {
        accounts: values,
        currentPage: 1,
        pageSize: 100,
      };
      $service('cloudim_user', 'fetch', params)
        .then(({ users }: IServiceCloudImUserFetchResult) => {
          users.forEach((user) => {
            this.usersMap[user.account || ''] = user;
          });
          // 回调
          onusers && onusers(this.usersMap);
          resolve({
            usersMap: this.usersMap,
          });
        })
        .catch((e) => {
          console.log('[e] ', e);
          reject();
        });
    });
  };

  /**
   * 同步会话
   */
  sessionList = new ListStore<ISession>({
    onFetch: (currentPage, pageSize = 20) => {
      return new Promise<Array<ISession>>((resolve, reject) => {
        const { token, account } = this.config;
        let params: IServiceCloudImSessionFetchParams = {
          token,
          account,
          currentPage,
          pageSize,
        };
        $service('cloudim_session', 'fetch', params)
          .then(async ({ sessions }: IServiceCloudImSessionFetchResult) => {
            console.log('[offline sessions] ', sessions);
            // 拉取用户名片
            await this.fetchUser(sessions.map((item) => item.to || ''));
            // 转换
            let _sessions = this.formatSessions(sessions);
            resolve(_sessions);
          })
          .catch((e) => {
            console.log('[e] ', e);
            reject();
          });
      });
    },
    onChange: ({ data }) => {
      this.config.onsessions && this.config.onsessions(data);
    },
  });
  syncSession = () => {
    this.sessionList.refresh();
  };
  loadNextSession = () => {
    this.sessionList.loadNext();
  };
  formatSessions = (sessions: ISession[]): Array<ISession> => {
    return sessions.map((session) => {
      session.sessionId = session.scene + '-' + session.to;
      return session;
    });
  };

  /**
   * 同步消息
   * 根据本地缓存消息最后的时间戳获取我发送的或发送给我的消息
   */
  fetchOfflineMsg = ({ to }: { to: string; limit: number }) => {
    const { token, account } = this.config;
    let params: IServiceCloudImMsgFetchParams = {
      from: account,
      to,
      limit: 100,
      reverse: 1,
      endtime: Date.now(),
    };
    $service('cloudim_msg', 'fetch', params).then(({ msgs }: IServiceCloudImMsgFetchResult) => {
      console.log('[offline msgs] ', msgs);
      const { onofflinemsgs } = this.config;
      if (msgs.length > 0) {
        onofflinemsgs && onofflinemsgs(msgs);
      }
    });
  };

  formatMessage = (message: IMessage): IMessage => {
    if (message._id && !message.idServer) message.idServer = message._id;
    // 补充消息方向
    message.flow = message.from === this.config.account ? 'out' : 'in';
    // 补充或调整 sessionId （每个人的sessionId不同）
    message.sessionId = message.scene + '-' + (message.flow === 'in' ? message.from : message.to);
    message.key = String(message.time) + message.idClient + message.status;
    if (message.type === 'image' || message.type === 'video') {
      // 图片缩放
      const { tempWidth, tempHeight } = imageScale(message.file.w, message.file.h, 240, 360);
      message.file.w = tempWidth;
      message.file.h = tempHeight;
    }
    return message;
  };

  /**
   * 发送消息
   */
  sendMessage = ({ msg, done }: { msg: IMessage; done: (error: IImError | null, newMsg: IMessage) => void }): IMessage => {
    const { account, token } = this.config;
    const { scene, to } = msg;
    let idClient = uuid();
    let sessionId = scene + '-' + to;
    let _msg = {
      from: account,
      time: Date.now(),
      ...msg,
      idClient,
      sessionId,
    };
    let params: IServiceCloudImMsgSendParams = {
      account,
      token,
      msg: _msg,
    };
    $service('cloudim_msg', 'send', params)
      .then((res: IServiceCloudImMsgSendResult) => {
        console.log('[cloudim_msg send res] ', res);
        const { msg, session } = res;
        msg.idClient = idClient;
        msg.sessionId = sessionId;
        msg.status = 'success';
        let _msg = this.formatMessage(msg);
        this.sessionChangeByMsg(_msg);
        done(null, msg);
      })
      .catch((e) => {
        console.log('[send err] ', e);
        // @TODO: 待完善发送失败之场景
        _msg.status = 'fail';
      });
    // 先返回消息体用以先展示
    _msg.status = 'sending';
    return this.formatMessage(_msg);
  };

  /** 发送文本消息 */
  sendText = ({ scene, to, text, pushPayload, done }: { scene: IScene; to: string; text: string; pushPayload: string; done: (error: IImError | null, newMsg: IMessage) => void }): IMessage => {
    console.log('[sendText] ', scene, to, text);
    let _msg: IMessage = {
      type: 'text',
      scene,
      to,
      text,
    };
    let msg = this.sendMessage({
      msg: _msg,
      done,
    });
    console.log('[sendMessage msg] ', msg);
    return msg;
  };

  /** 发送位置消息 */
  sendGeo = ({ scene, to, geo, pushPayload, done }: { scene: string; to: string; geo: IImGeo; pushPayload: string; done: (error: IImError, newMsg: IMessage) => void }): IMessage => {
    $service('cloudim_msg', 'send', {});
    return {} as IMessage;
  };

  /** 发送文件消息 */
  sendFile = ({
    type,
    scene,
    to,
    file,
    pushPayload,
    done,
  }: {
    type: 'image' | 'video' | 'audio' | 'file';
    scene: IScene;
    to: string;
    file: IMessageFile;
    pushPayload: string;
    done: (error: IImError | null, newMsg: IMessage) => void;
  }): IMessage => {
    console.log('[sendFile] ', scene, to, file);
    let _msg: IMessage = {
      type,
      scene,
      to,
      file: file as any,
    };
    let msg = this.sendMessage({
      msg: _msg,
      done,
    });
    console.log('[sendMessage msg] ', msg);
    return msg;
  };

  /**
   * 发送提醒消息
   *  * 提醒消息是消息类型的一种
   *  * 提醒消息用于会话内的状态提醒，如进入会话时出现的欢迎消息，或者会话命中敏感词后的提示消息等等.
   */
  sendTipMsg = ({
    isLocal,
    scene,
    to,
    tip,
    time,
    pushPayload,
    done,
  }: {
    isLocal: boolean;
    scene: IMessage['scene'];
    to: string;
    tip: IMessage['tip'];
    time: number;
    pushPayload?: string;
    done: (error: IImError, newMsg: IMessage) => void;
  }): IMessage => {
    return {} as IMessage;
  };

  /** 发送自定义消息 */
  sendCustomMsg = ({ scene, to, content, pushPayload, done }: { scene: string; to: string; content: object; pushPayload: string; done: (error: IImError, newMsg: IMessage) => void }): IMessage => {
    return {} as IMessage;
  };

  /** 重发消息 */
  resendMsg = ({ msg, done }: { msg: IMessage; done: (err: Error, newMsg: IMessage, data?: object) => void }) => {};

  /** 发送自定义消息 */
  deleteMsg = ({ msg, done }: { msg: IMessage; done: (error: IImError, result?: object) => void }): IMessage => {
    return {} as IMessage;
  };

  /** 删除消息 */
  deleteLocalMsg = ({ msg, done }: { msg: IMessage; done: (err: IImError | null, data?: object) => void }) => {
    const { account, token } = this.config;
    if (msg._id) {
      let params: IServiceCloudImMsgDeleteParams = {
        account,
        token,
        id: msg._id
      }
      $service('cloudim_msg', 'deleteMsg', params).then((res) => {
        console.log('[deleteMsg] ', res);
      }).catch((e) => {
        done && done(e);
      })
    }
  };

  deleteLocalMsgsBySession = ({ scene, to, done }: { scene: string; to: string; done: (err: IImError) => void }) => {};

  /**
   * 消息收到回执
   */
  sendMsgReceipt = ({ msg, done }: { msg: IMessage; done?: (error: Error) => void }) => {
    const { account, client } = this.config;
    this.sendToServer({
      type: 'msgRcvAck',
      account,
      client,
      msg
    });
  };

  getLocalMsgs = ({
    sessionId,
    scene,
    end,
    limit,
    to,
    done,
  }: {
    scene: IScene;
    to: string;
    sessionId: string;
    end?: number;
    limit: number;
    desc: boolean;
    done: (err: Error | null, msgs: { msgs: IMessage[] }) => void;
  }) => {
    const { token, account } = this.config;
    let params: IServiceCloudImMsgFetchParams = {
      from: account,
      to,
      limit,
      reverse: 1,
      endtime: end || Date.now(),
    };
    $service('cloudim_msg', 'fetch', params).then(({ msgs }: IServiceCloudImMsgFetchResult) => {
      done(null, {
        msgs: msgs.map((item) => this.formatMessage(item)),
      });
    });
  };

  getHistoryMsgs = ({
    scene,
    to,
    endTime,
    reverse,
    asc,
    limit,
    done,
  }: {
    scene: IScene;
    to: string;
    endTime: number;
    reverse: boolean;
    asc?: boolean;
    limit: number;
    desc?: boolean;
    done: (err: Error, msgs: { msgs: IMessage[] }) => void;
  }) => {};

  getLocalSysMsgs = ({ category }: { category: 'friend'; read: boolean; limit: number; done: (err: Error, msgs: { sysMsgs: ISystemMessage[] }) => void }) => {};

  deleteLocalSysMsg = ({ id, done }: { id: string; done: (err: object) => void }) => {};

  sessionChangeByMsg = (message: IMessage) => {
    let flow = message.flow;
    let to = flow === 'in' ? message.from : message.to;
    // this.sessionList.setData((data) => {
    //   let sessionIndex = data.findIndex(session => session.to === to);
    //   if (sessionIndex > -1) {
    //     let session = data[sessionIndex];
    //     session.lastMsg = message;
    //     session.updateTime = message.time;
    //     if (this.currSessionId !== session.sessionId) {
    //       session.unread = (session.unread || 0) + 1;
    //     }
    //     data[sessionIndex] = session;
    //   } else {
    //     // @TODO: 会话不存在时（新会话、分页未加载到） 应该接收来自服务器的session
    //   }
    //   return data;
    // });
  };

  mergeSessions = (sessions: ISession[], newSessions: ISession[]): Array<ISession> => {
    for (let i = 0; i < sessions.length; i++) {
      for (let j = 0; j < newSessions.length; j++) {
        if (sessions[i].to === newSessions[j].to) {
          sessions.splice(i, 1);
        }
      }
    }
    return sessions.concat(newSessions);
  };

  currSessionId = '';
  setCurrSession = (sessionId: string) => {
    this.currSessionId = sessionId;
    this.sendToServer({
      type: 'setCurrSession',
      account: this.config.account,
      sessionId,
    });
  };

  resetCurrSession = () => {
    this.currSessionId = '';
  };

  deleteSession = ({ scene, to, done }: { scene: string; to: string; done: (err: object, res: object) => void }) => {
    this.sendToServer({
      type: 'invokeDeleteSession',
      account: this.config.account,
      scene,
      to
    });
  };

  deleteLocalSession = ({ id, done }: { id: string; done: (err: object, res: object) => void }) => {};

  cutSessionsByIds = (sessions: ISession[], removeSessions: string[]) => {};

  /* 新增会话置顶 */
  addStickTopSession = ({
    id,
    topCustom
  }: {
    /* 会话 sessionId */
    id: string;
    topCustom?: object;
  }) => {
    this.sendToServer({
      type: 'updateSession',
      account: this.config.account,
      sessionId: id,
      isTop: true,
      topCustom
    });
  }
  /* 更新会话置顶的扩展字段 */
  updateStickTopSession = ({
    id,
    topCustom
  }: {
    /* 会话 sessionId */
    id: string;
    topCustom?: object;
  }) => {
    this.sendToServer({
      type: 'updateSession',
      account: this.config.account,
      sessionId: id,
      topCustom
    });
  }
  /* 删除会话置顶 */
  deleteStickTopSession = ({
    id,
  }: {
    /* 会话 sessionId */
    id: string;
  }) => {
    this.sendToServer({
      type: 'updateSession',
      account: this.config.account,
      sessionId: id,
      isTop: false,
    });
  }
  /* 获取置顶会话列表 */
  getStickTopSessions = () => {
    
  }

  /**
   * 获取黑名单和静音列表
   * 如果开发者在初始化SDK的时候设置了syncRelations为false, 那么就收不到onblacklist和onmutelist回调, 可以调用此接口来获取黑名单和静音列表。
   */
  getRelations = ({
    done,
  }: {
    done: (
      err: object,
      res: {
        blacklist: IBlackItem[];
        mutelist: IMuteItem[];
      },
    ) => void;
  }) => {};

  /**
   * 加入静音列表/从静音列表移除
   */
  mutelist: IMuteItem[] = [];
  getMuteList = () => {
    const { account, onmutelist } = this.config;
    let user = this.usersMap[account];
    console.log('[getMuteList] ', user);
    if (user && user.mutelist) {
      onmutelist && onmutelist(user.mutelist);
    }
  };
  markInMutelist = ({ account, isAdd, done }: { account: string; isAdd: boolean; done: (err: IImError | null, res?: object) => void }) => {
    const { onmutelist } = this.config;
    let mutelist = this.mutelist;
    let index = mutelist.findIndex((item) => item.account === account);
    if (index > -1) {
      mutelist.splice(index, 1);
    } else {
      mutelist.push({
        account,
        create_at: Date.now(),
      });
    }
    this.updateMyInfo({
      mutelist,
      done,
    });
    onmutelist && onmutelist(mutelist);
  };

  /**
   * 加入静音列表/从静音列表移除
   */
  blacklist: IBlackItem[] = [];
  getBlackList = () => {
    const { account, onblacklist } = this.config;
    let user = this.usersMap[account];
    if (user && user.blacklist) {
      onblacklist && onblacklist(user.blacklist);
    }
  };
  markInBlacklist = ({ account, isAdd, done }: { account: string; isAdd: boolean; done: (err: IImError | null, res?: object) => void }) => {
    const { onblacklist } = this.config;
    let blacklist = this.blacklist;
    let index = blacklist.findIndex((item) => item.account === account);
    if (index > -1) {
      blacklist.splice(index, 1);
    } else {
      blacklist.push({
        account,
        create_at: Date.now(),
      });
    }
    this.updateMyInfo({
      blacklist,
      done,
    });
    onblacklist && onblacklist(blacklist);
  };

  /**  */
  previewFile = ({
    type,
    filePath,
    uploadprogress,
    done,
  }: {
    type: 'image' | 'audio' | 'video';
    filePath: string;
    uploadprogress: (info: { total: number; loaded: number; percentage: number; percentageText: string }) => void;
    done: (err: IImError | null, fileInfo: { name: string; url: string; ext?: string }) => void;
  }) => {
    let name = uuid();
    $uploadBigFile({
      t: type === 'image' ? 1 : type === 'video' ? 2 : type === 'audio' ? 3 : 1,
      name,
      type: type === 'image' ? 'image/jpeg' : type === 'video' ? 'video/mp4' : type === 'audio' ? 'audio/aac' : 'image/jpeg',
      filePath,
      onProgress: ({ progress, totalBytesSent, totalBytesExpectedToSend }) => {
        console.log('[progress] ', progress, totalBytesSent, totalBytesExpectedToSend);
        uploadprogress &&
          uploadprogress({
            total: totalBytesExpectedToSend,
            loaded: totalBytesSent,
            percentage: progress,
            percentageText: progress + '%',
          });
      },
    }).then(({ hash }) => {
      done &&
        done(null, {
          name,
          url: hash,
        });
    });
  };

  updateMyInfo = ({
    nick,
    avatar,
    mutelist,
    blacklist,
    pushid,
    done,
  }: {
    nick?: string;
    avatar?: string;
    pushid?: string;
    mutelist?: IUser['mutelist'];
    blacklist?: IUser['blacklist'];
    done?: (err: IImError | null, user?: IUser) => void;
  }) => {
    let params: IServiceCloudImUserUpdateParams = {
      account: this.config.account,
      token: this.config.token,
      nick,
      avatar,
    };
    if (pushid) params.pushid = pushid;
    if (blacklist) params.blacklist = blacklist;
    if (mutelist) params.mutelist = mutelist;
    $service('cloudim_user', 'updateUinfo', params)
      .then(() => {
        this.fetchUser([this.config.account])
          .then(({ usersMap }) => {
            done && done(null, usersMap[this.config.account]);
          })
          .catch((e) => {
            done && done(e);
          });
      })
      .catch((e) => {
        done && done(e);
      });
  };

  logout = ({ done }: { done: (error?: object) => void }) => {
    // 断开连接
    this.socket?.disconnect();
    setTimeout(() => {
      done();
    }, 500);
  };
}

export default Mim;

export enum MimConnectState {
  'connecting' = '正在连接',
  'connect' = '已连接',
  'disconnect' = '已断开连接',
  'connect_error' = '连接出错',
  'connect_timeout' = '连接超时',
  'reconnect' = '已重连',
  'reconnecting' = '重连中',
  'reconnect_failed' = '重连失败',
  'reconnect_error' = '重连出错',
  'reconnect_attempt' = '重连出错',
}
